/*
 * GridLayout.java  0.4.0 / Oct 24, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing.layouts;

import com.cute4j.swing.Component;
import com.cute4j.swing.Container;
import com.cute4j.swing.graphics.Dimension;
import com.cute4j.swing.graphics.Insets;

/**
 * Grid Layout.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class GridLayout implements LayoutManager {

// Private Fields. -------------------------------------------------------------

  private int columns;
  private int rows;
  private int hgap;
  private int vgap;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new GridLayout object.
   */
  public GridLayout() {
    this(1, 0, 0, 0);
  }

  /**
   * Constructs new GridLayout object.
   *
   * @param rows the number of rows
   * @param columns the number of columns
   */
  public GridLayout(int rows, int columns) {
    this(rows, columns, 0, 0);
  }

  /**
   * Constructs new GridLayout object.
   *
   * @param rows the number of rows
   * @param columns the number of columns
   * @param hgap the horizontal gap between components
   * @param vgap the vertical gap between components
   */
  public GridLayout(int rows, int columns, int hgap, int vgap) {
    if (rows < 0) {
      throw new IllegalArgumentException("Negative number of rows");
    }
    if (columns < 0) {
      throw new IllegalArgumentException("Negative number of columns");
    }
    if ((rows == 0) && (columns == 0)) {
      throw new IllegalArgumentException("Both rows and columns are zero");
    }
    if (hgap < 0) {
      throw new IllegalArgumentException("Negative horizontal gap");
    }
    if (vgap < 0) {
      throw new IllegalArgumentException("Negative vertical gap");
    }
    this.rows = rows;
    this.columns = columns;
    this.hgap = hgap;
    this.vgap = vgap;
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the number of rows.
   *
   * @return the number of rows
   */
  public int getRows() {
    return rows;
  }

  /**
   * Sets the number of rows.
   *
   * @param rows the number of rows
   */
  public void setRows(int rows) {
    if (rows < 0) {
      throw new IllegalArgumentException("Negative number of rows");
    }
    if ((rows == 0) && (columns == 0)) {
      throw new IllegalArgumentException("Both rows and columns are zero");
    }
    this.rows = rows;
  }

  /**
   * Returns the number of columns.
   *
   * @return the number of columns
   */
  public int getColumns() {
    return columns;
  }

  /**
   * Sets the number of columns.
   *
   * @param columns the number of columns
   */
  public void setColumns(int columns) {
    if (columns < 0) {
      throw new IllegalArgumentException("Negative number of rows");
    }
    if ((rows == 0) && (columns == 0)) {
      throw new IllegalArgumentException("Negative number of columns");
    }
    this.columns = columns;
  }

  /**
   * Returns the horizontal gap between components.
   *
   * @return the horizontal gap between components
   */
  public int getHgap() {
    return hgap;
  }

  /**
   * Sets the horizontal gap between components.
   *
   * @param hgap the horizontal gap between components
   */
  public void setHgap(int hgap) {
    this.hgap = hgap;
  }

  /**
   * Returns the vertical gap between components.
   *
   * @return the vertical gap between components
   */
  public int getVgap() {
    return vgap;
  }

  /**
   * Sets the vertical gap between components.
   *
   * @param vgap the vertical gap between components
   */
  public void setVgap(int vgap) {
    this.vgap = vgap;
  }

// LayoutManager Interface Implementation. -------------------------------------

  /**
   * {@inheritDoc}
   */
  public void addLayoutComponent(Component comp, Object constraints) {
    // empty implementation
  }

  /**
   * {@inheritDoc}
   */
  public void removeLayoutComponent(Component comp) {
    // empty implementation
  }

  /**
   * {@inheritDoc}
   */
  public Dimension preferredLayoutSize(Container target) {
    int componentCount = target.getComponentCount();
    int rows = this.rows;
    int columns = this.columns;
    if (rows > 0) {
      columns = (componentCount + rows - 1) / rows;
    } else {
      rows = (componentCount + columns - 1) / columns;
    }
    int width = 0;
    int height = 0;
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getPreferredSize();
        if (width < d.getWidth()) {
          width = d.getWidth();
        }
        if (height < d.getHeight()) {
          height = d.getHeight();
        }
      }
    }
    Insets insets = target.getInsets();
    return new Dimension(insets.getLeft() + insets.getRight() +
                         columns * width + (--columns) * hgap,
                         insets.getTop() + insets.getBottom() + rows * height +
                         (--rows) * vgap);
  }

  /**
   * {@inheritDoc}
   */
  public Dimension maximumLayoutSize(Container target) {
    int componentCount = target.getComponentCount();
    int rows = this.rows;
    int columns = this.columns;
    if (rows > 0) {
      columns = (componentCount + rows - 1) / rows;
    } else {
      rows = (componentCount + columns - 1) / columns;
    }
    int width = 0;
    int height = 0;
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getMaximumSize();
        if (width < d.getWidth()) {
          width = d.getWidth();
        }
        if (height < d.getHeight()) {
          height = d.getHeight();
        }
      }
    }
    Insets insets = target.getInsets();
    return new Dimension(insets.getLeft() + insets.getRight() +
                         columns * width + (--columns) * hgap,
                         insets.getTop() + insets.getBottom() + rows * height +
                         (--rows) * vgap);
  }

  /**
   * {@inheritDoc}
   */
  public Dimension minimumLayoutSize(Container target) {
    int componentCount = target.getComponentCount();
    int rows = this.rows;
    int columns = this.columns;
    if (rows > 0) {
      columns = (componentCount + rows - 1) / rows;
    } else {
      rows = (componentCount + columns - 1) / columns;
    }
    int width = 0;
    int height = 0;
    for (int i = 0; i < componentCount; i++) {
      Component component = target.getComponent(i);
      if (component.isDisplayable()) {
        Dimension d = component.getMinimumSize();
        if (width < d.getWidth()) {
          width = d.getWidth();
        }
        if (height < d.getHeight()) {
          height = d.getHeight();
        }
      }
    }
    Insets insets = target.getInsets();
    return new Dimension(insets.getLeft() + insets.getRight() +
                         columns * width + (--columns) * hgap,
                         insets.getTop() + insets.getBottom() + rows * height +
                         (--rows) * vgap);
  }

  /**
   * {@inheritDoc}
   */

  public void layoutContainer(Container target) {
    int componentCount = target.getComponentCount();
    if (componentCount > 0) {
      int rows = this.rows;
      int columns = this.columns;
      if (rows > 0) {
        columns = (componentCount + rows - 1) / rows;
      } else {
        rows = (componentCount + columns - 1) / columns;
      }
      Insets insets = target.getInsets();
      int width = (target.getWidth() - insets.getLeft() - insets.getRight() -
                   (columns - 1) * hgap) / columns;
      int height = (target.getHeight() - insets.getTop() - insets.getBottom() -
                    (rows - 1) * vgap) / rows;
      int widthHGap = width + hgap;
      int heightVGap = height + vgap;
      if (target.isLeftToRight()) {
        for (int column = 0, x = insets.getLeft(); column < columns;
             column++, x += widthHGap) {
          for (int row = 0, y = insets.getTop(); row < rows;
               row++, y += heightVGap) {
            int i = row * columns + column;
            if (i < componentCount) {
              Component component = target.getComponent(i);
              if (component.isDisplayable()) {
                component.setBounds(x, y, width, height);
              }
            }
          }
        }
      } else {
        for (int column = 0, x = target.getWidth() - insets.getRight() - width;
             column < columns; column++, x -= widthHGap) {
          for (int row = 0, y = insets.getTop(); row < rows;
               row++, y += heightVGap) {
            int i = row * columns + column;
            if (i < componentCount) {
              Component component = target.getComponent(i);
              if (component.isDisplayable()) {
                component.setBounds(x, y, width, height);
              }
            }
          }
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void invalidateLayout(Container target) {
    // empty method body
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public String toString() {
    return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap +
           ",rows=" + rows + ",columns=" + columns + "]";
  }

}
